Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Squiz/ClassFileName: various improvements #845

Merged
merged 6 commits into from
Mar 7, 2025

Conversation

jrfnl
Copy link
Member

@jrfnl jrfnl commented Feb 25, 2025

Description

Squiz/ClassFileName: expand the tests

This commit adds some extra tests for the sniff.

  • Document handling of unfinished OO declarations (which contain enough info to still action).
  • Safeguard that comments in unexpected places doesn't cause problems.

As this sniff looks at the file name of the file containing the tests, we cannot use the normal SniffNameUnitTest.#.inc naming conventions for extra tests files, so the getTestFiles() method from the AbstractSniffUnitTest class needs to be overloaded to retrieve the test case files in a slightly different way.

Squiz/ClassFileName: minor stability tweak

Use the predefined Tokens::$ooScopeTokens array to automatically benefit from new OO structures being added to that list.

Squiz/ClassFileName: bow out earlier for STDIN

As STDIN won't contain a usable file name, bow out and don't examine the input again.

Note: the process() method should really end on a return $this->phpcsFile->numTokens; as well as a file containing multiple classes should not get multiple contradicting errors, but that would require each test for this sniff to be in their own file, which I currently don't deem worth the effort.

Squiz/ClassFileName: don't error when there is no class name

As things were, the sniff would try to find the next T_STRING, but didn't take live coding into account, which means it would end up comparing $tokens[false]['content'] to the filename, which would end up comparing <?php to the file name.

This changes the sniff to use the File::getDeclarationName() method instead and verifies we have a usable name to compare against before throwing the error.

Includes test.

Squiz/ClassFileName: inverse the error message

As things were, the Squiz.Classes.ClassFileName sniff would recommend for a class (OO structure) to be called the same as the file.

However, file names can contain characters which are not allowed in PHP class names, so this advise could end up being impossible to action for names not complying with the PHP requirements for symbol names.

This commit changes the error message to advise to change the file name instead of the class name, which should make the error actionable in all cases.

Includes tests.
While the tests would not fail without this change, the tests can be used to verify the difference in the error messages on the command-line.

Before this change, the errors would read like so:

FILE: src/Standards/Squiz/Tests/Classes/ClassFileName Spaces In FilenameUnitTest.inc
------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Class name doesn't match filename; expected "class ClassFileName Spaces In FilenameUnitTest" (Squiz.Classes.ClassFileName.NoMatch)
------------------------------------------------------------------------------------------------------------------------------------------------

FILE: src/Standards/Squiz/Tests/Classes/ClassFileName-Dashes-In-FilenameUnitTest.inc
------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Class name doesn't match filename; expected "class ClassFileName-Dashes-In-FilenameUnitTest" (Squiz.Classes.ClassFileName.NoMatch)
------------------------------------------------------------------------------------------------------------------------------------------------

With this change, the errors will read:

FILE: src/Standards/Squiz/Tests/Classes/ClassFileName Spaces In FilenameUnitTest.inc
-----------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Filename doesn't match class name; expected file name "ClassFileNameSpacesInFilenameUnitTest.inc" (Squiz.Classes.ClassFileName.NoMatch)
-----------------------------------------------------------------------------------------------------------------------------------------------------

FILE: src/Standards/Squiz/Tests/Classes/ClassFileName-Dashes-In-FilenameUnitTest.inc
-----------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Filename doesn't match class name; expected file name "ClassFileNameDashesInFilenameUnitTest.inc" (Squiz.Classes.ClassFileName.NoMatch)
-----------------------------------------------------------------------------------------------------------------------------------------------------

Side-note: while the strrpos($fullPath, '.') could return false, this is not something we need to be concerned about in this sniff as PHPCS doesn't examine files without a file extension (at this time), so there should always be a . in the file name.

🆕 Squiz/ClassFileName: use more meaningful variable names

Suggested changelog entry

Changed: Squiz.Classes.ClassFileName: recommend changing the file name instead of changing the class name to prevent recommendations which are inactionable due to the file name not translating to a valid PHP symbol name
Fixed: Squiz.Classes.ClassFileName: don't throw an incorrect error for an unfinished OO declaration during live coding

Related issues/external references

Inspired by #843

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Copy link
Contributor

@braindawg braindawg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Commented on the only minor quibble I could find. Nothing impacting the functionality, so it's fine to merge as-is. Thanks!

jrfnl added 6 commits March 7, 2025 13:54
This commit adds some extra tests for the sniff.
* Document handling of unfinished OO declarations (which contain enough info to still action).
* Safeguard that comments in unexpected places doesn't cause problems.

As this sniff looks at the file name of the file containing the tests, we cannot use the _normal_ `SniffNameUnitTest.#.inc` naming conventions for extra tests files, so the `getTestFiles()` method from the `AbstractSniffUnitTest` class needs to be overloaded to retrieve the test case files in a slightly different way.
Use the predefined `Tokens::$ooScopeTokens` array to automatically benefit from new OO structures being added to that list.
As `STDIN` won't contain a usable file name, bow out and don't examine the input again.

_Note: the `process()` method should really end on a `return $this->phpcsFile->numTokens;` as well as a file containing multiple classes should not get multiple contradicting errors, but that would require each test for this sniff to be in their own file, which I currently don't deem worth the effort._
As things were, the sniff would try to find the next `T_STRING`, but didn't take live coding into account, which means it would end up comparing `$tokens[false]['content']` to the filename, which would end up comparing `<?php` to the file name.

This changes the sniff to use the `File::getDeclarationName()` method instead and verifies we have a usable name to compare against before throwing the error.

Includes test.
As things were, the `Squiz.Classes.ClassFileName` sniff would recommend for a class (OO structure) to be called the same as the file.

However, file names can contain characters which are not allowed in PHP class names, so this advise could end up being impossible to action for names not complying with the PHP requirements for symbol names.

This commit changes the error message to advise to change the file name instead of the class name, which should make the error actionable in all cases.

Includes tests.
While the tests would not _fail_ without this change, the tests can be used to verify the difference in the error messages on the command-line.

Before this change, the errors would read like so:
```
FILE: src/Standards/Squiz/Tests/Classes/ClassFileName Spaces In FilenameUnitTest.inc
------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Class name doesn't match filename; expected "class ClassFileName Spaces In FilenameUnitTest" (Squiz.Classes.ClassFileName.NoMatch)
------------------------------------------------------------------------------------------------------------------------------------------------

FILE: src/Standards/Squiz/Tests/Classes/ClassFileName-Dashes-In-FilenameUnitTest.inc
------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Class name doesn't match filename; expected "class ClassFileName-Dashes-In-FilenameUnitTest" (Squiz.Classes.ClassFileName.NoMatch)
------------------------------------------------------------------------------------------------------------------------------------------------
```

With this change, the errors will read:
```
FILE: src/Standards/Squiz/Tests/Classes/ClassFileName Spaces In FilenameUnitTest.inc
-----------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Filename doesn't match class name; expected file name "ClassFileNameSpacesInFilenameUnitTest.inc" (Squiz.Classes.ClassFileName.NoMatch)
-----------------------------------------------------------------------------------------------------------------------------------------------------

FILE: src/Standards/Squiz/Tests/Classes/ClassFileName-Dashes-In-FilenameUnitTest.inc
-----------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------------------------------------------
 7 | ERROR | Filename doesn't match class name; expected file name "ClassFileNameDashesInFilenameUnitTest.inc" (Squiz.Classes.ClassFileName.NoMatch)
-----------------------------------------------------------------------------------------------------------------------------------------------------
```

Side-note: while the `strrpos($fullPath, '.')` _could_ return `false`, this is not something we need to be concerned about in this sniff as PHPCS doesn't examine files without a file extension (at this time), so there should always be a `.` in the file name.
@jrfnl jrfnl force-pushed the feature/squiz-classfilename-various-improvements branch from c1cf608 to cf5d4c1 Compare March 7, 2025 12:56
@jrfnl jrfnl added this to the 3.12.0 milestone Mar 7, 2025
@jrfnl jrfnl merged commit d78846e into master Mar 7, 2025
59 checks passed
@jrfnl jrfnl deleted the feature/squiz-classfilename-various-improvements branch March 7, 2025 13:26
braindawg added a commit to braindawg/PHP_CodeSniffer that referenced this pull request Mar 7, 2025
Per PR PHPCSStandards#845, the filename must be modified to match the class name,
since class names are much more restrictive than filenames and the
inverse is not always possible.

This clarifies in the example titles that the fix for the invalid code
is to modify the filename rather than the class name.
jrfnl pushed a commit that referenced this pull request Mar 7, 2025
* Add XML doc for Squiz\Classes\ClassFileName sniff

* [Doc] Emphasize sniff focus for ClassFileName

Adjusts the `<em>` tags in the Squiz.Classes.ClassFileName sniff XML
docs to better highlight the parts of the code samples that illustrate
the purpose of the sniff.

* Correct file/class name relation in sniff doc

Per PR #845, the filename must be modified to match the class name,
since class names are much more restrictive than filenames and the
inverse is not always possible.

This clarifies in the example titles that the fix for the invalid code
is to modify the filename rather than the class name.
jrfnl pushed a commit that referenced this pull request Mar 7, 2025
* Add XML doc for Squiz\Classes\ClassFileName sniff

* [Doc] Emphasize sniff focus for ClassFileName

Adjusts the `<em>` tags in the Squiz.Classes.ClassFileName sniff XML
docs to better highlight the parts of the code samples that illustrate
the purpose of the sniff.

* Correct file/class name relation in sniff doc

Per PR #845, the filename must be modified to match the class name,
since class names are much more restrictive than filenames and the
inverse is not always possible.

This clarifies in the example titles that the fix for the invalid code
is to modify the filename rather than the class name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants